/*
 * This program is free software.
 * Code may be used or distributed under GPL.
 * (C) 2010 Ze Ji @ University of Hertfordshire  
 *
 *   Source File : SerialPortOct.cc
 */

#define __ZJDEBUG__

/*Octave related included files*/
#include <octave/oct.h>
#include <octave/parse.h>
#include <octave/dynamic-ld.h>
#include <octave/oct-map.h>
#include <octave/oct-stream.h>
#include <octave/ov-base-scalar.h>

/*General included files*/
#include <iostream>
#include <vector>
#include <string>

/*Serial Port Communication included files*/
#include <SerialPort.h>
#include <SerialStream.h>

using namespace std;

class SerialPortOct: public octave_base_value
{
public:
  /* Well the constructor!! */
  SerialPortOct(void):octave_base_value()
  {
    port = "";
  }
  
  SerialPortOct(octave_value sp_val):octave_base_value()
  {
    const octave_base_value& rep = sp_val.get_rep();
    sp = ((const SerialPortOct &) rep).getSerialPort();
  }
  
  SerialPortOct(string port):octave_base_value()
  {
    this->port = port;
    sp = new SerialPort(this->port);
  }
  
  ~SerialPortOct(void)
  {
    if(sp->IsOpen())
      sp->Close();
    delete sp;
  }
  
  void print (std::ostream& os, bool pr_as_read_syntax = false) const
  {
    os << "Serial Port: " << port << endl;
  }
  
  bool is_constant (void) const { return true; }
  bool is_defined (void) const { return true; }
  
  SerialPort *getSerialPort(void) const 
  {
    return sp;
  }
    
bool open(void)
  {
    try
      {
	sp->Open();
      }	
    catch(SerialPort::AlreadyOpen al)
      {
	cout << al.what() << endl;
	return false;
      }
    catch(SerialPort::OpenFailed of)
      {
	cout << of.what() << endl;
	return false;
      }
    catch(std::invalid_argument ia)
      {
	cout << ia.what() << endl;
	return false;
      }
    catch(...)
      {
	return false;
      }
    return true;
  }
  
  bool close(void)
  {
    try
      {
	sp->Close();
      }
    catch(...)
      {
	cout << "Error. " << endl;
	return false;
      }
    return true;
  }
  
  bool write(vector<unsigned char> *buf)
  {
    try{
      sp->Write(*buf);
    }
    catch(SerialPort::NotOpen no)
      {
	cout << no.what() << endl;
	return false;
      }
    catch(std::runtime_error re)
      {
	cout << re.what() << endl;
	return false;
      }
    catch(...)
      {
	cout << "Unknown error when writing to serial port. " << endl;
	return false;
      }
    return true;
  }

  bool readByte(unsigned char *c)
  {
    try{
      *c = sp->ReadByte();
    }
    catch(SerialPort::NotOpen no)
      {
	cout << no.what() << endl;
	return false;
      }
    catch(SerialPort::ReadTimeout rto)
      {
	cout << rto.what() << endl;
	return false;
      }
    catch(std::runtime_error re)
      {
	cout << re.what() << endl;
	return false;
      }
    return true;
  }

  bool readLine(string *s)
  {
    try{
      *s = sp->ReadLine();  // line terminator and timeout should be
			    // assigned later
    }
    catch(SerialPort::NotOpen no)
      {
	cout << no.what() << endl;
	return false;
      }
    catch(SerialPort::ReadTimeout rto)
      {
	cout << rto.what() << endl;
	return false;
      }
    catch(std::runtime_error re)
      {
	cout << re.what() << endl;
	return false;
      }
    return true;
  }

private:
  SerialPort *sp;
  string port;
  bool isOpen;
  
  DECLARE_OCTAVE_ALLOCATOR
  
  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
};


DEFINE_OCTAVE_ALLOCATOR (SerialPortOct);
DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (SerialPortOct, "SerialPortOct","SerialPortOct");

DEFUN_DLD(SerialPortMake,args,,"SerialPortMake(string port)\n\
 makes new instances of SerialPortOct objects \
to fill into the world of octave")
{
  if(args.length() != 1)
    {
      cout << "usage: SerialPortMake(string port)";
      return octave_value_list();
      //return octave_value("Error: Format");
    }
  SerialPortOct *sp = new SerialPortOct(args(0).string_value());
  return octave_value(sp);
}

DEFUN_DLD(SerialPortOpen, args, , "SerialPortOpen(SerialPortOct serialPort)")
{
  if(args.length() != 1)
    {
      error( "usage: SerialPortOpen(SerialPortOct)");
      return octave_value_list();
    }
  
  const octave_base_value& rep = args(0).get_rep();
  //SerialPortOct spo = ((SerialPortOct&)rep);
  return octave_value(((SerialPortOct&)rep).open());
}

DEFUN_DLD(SerialPortClose, args, , "SerialPortClose(SerialPortOct serialPort)")
{
  if(args.length() != 1)
    {
      error("usage: SerialPortClose(SerialPortOct)");
      return octave_value_list();
    }
  
  const octave_base_value& rep = args(0).get_rep();
  SerialPortOct spo = ((SerialPortOct&)rep);
  octave_value ret(spo.close());
  // not sure if this would work
  delete &spo;
  return ret;
}

DEFUN_DLD(SerialPortWrite, args, , "SerialPortWrite(SerialPortOct serialPort, buf")
{
  if(args.length() != 2)
    {
      cout << "usage: SerialPortWrite(SerialPortOct, buffer), \
buf is the data buffer to write to serial port. buf will be converted to vector of char" << endl;
      return octave_value_list();
    }
  bool success = true;

  /*
  //  charArray b = (charArray)args(1).array_value();
  string b = args(1).string_value();
  //cout << "b  has row of " << b.rows() << " and column of " <<
  //b.columns() << endl;
  
#ifdef __ZJDEBUG__
  cout << b << endl;
#endif
  
  int i = 0;
  vector<unsigned char> *buf = new vector<unsigned char>;
  
  for (i = 0; i < b.length(); i++)
   {
     //     cout << b.at(i) << endl;
     buf->push_back(b.at(i));
   }
  */
  const octave_base_value& rep = args(0).get_rep();
  SerialPortOct& spo = ((SerialPortOct&)rep);
 
  /******Try second solution****Temporary********************/
  uint8NDArray dd = args(1).uint8_array_value();
  cout  << "b has row of " << dd.rows() << " and column of " << dd.columns() << endl;
  if (!dd.is_vector())
    {
      cout << "usage: SerialPortWrite(SerialPortOct, buffer), \
buf is the data buffer to write to serial port. buf will be converted to vector of char" << endl;
      return octave_value(-1);
    }
  int i = 0;
  vector<unsigned char> *buf = new vector<unsigned char>;
  for(i = 0; i < dd.length(); i++)
    {
      buf->push_back(dd(i));
    }
  /*******************end of second solution*****************/
  return octave_value(spo.write(buf));
}

DEFUN_DLD(SerialPortReadByte, args, , "SerialPortReadByte(serialportoject)")
{
  if(args.length() != 1)
    {
      cout << "usage: SerialPortReadByte(SerialPortOct)" << endl;
      return octave_value_list();
    }
  
  const octave_base_value& rep = args(0).get_rep();
  SerialPortOct& spo = ((SerialPortOct&)rep);
  unsigned char *c;
  if(spo.readByte(c))
    return octave_value(*c);
  return octave_value("Could not read from serial port. Check for problems.");
}

DEFUN_DLD(SerialPortReadLine, args, , "SerialPortReadLine(serialportoject)")
{
  if(args.length() != 1)
    {
      cout << "usage: SerialPortReadByte(SerialPortOct)" << endl;
      return octave_value_list();
    }
  
  const octave_base_value& rep = args(0).get_rep();
  SerialPortOct& spo = ((SerialPortOct&)rep);
  string *s;
  if(spo.readLine(s))
    return octave_value(*s);
  return octave_value("Could not read from serial port. Check for problems.");
}

/*
DEFUN_DLD(SerialPortTest,args,,"SerialPortTest(serialportobject)\
 tests if the SerialPortOct object is present in the		\
saved pointer list present")
{
  if(args.length() < 1 
     || args(0).type_id()!=SerialPortOct::static_type_id())
    {
      cout<<"usage: SerialPortTest(Serialportobject)"<<endl;
      return octave_value(-1);
    }
  const octave_base_value& rep = args(0).get_rep();
  const SerialPortOct& b = ((const SerialPortOct &)rep);
  //b.print(cout,0);
  return octave_value();
}
*/
/*
  gcc --shared -o SerialPortMake.oct SerialPortoct.cc -I /usr/include/octave-3.2.2/octave/ -I /usr/include/octave-3.2.2/ -lserial
*/
